package com.softserveinc.ita.kaiji.service;

import java.util.*;

import com.softserveinc.ita.kaiji.model.User;
import com.softserveinc.ita.kaiji.model.UserImpl;
import com.softserveinc.ita.kaiji.model.game.*;
import com.softserveinc.ita.kaiji.model.player.PlayerFactory;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.softserveinc.ita.kaiji.dto.GameInfoDto;
import com.softserveinc.ita.kaiji.model.player.bot.Bot;
import com.softserveinc.ita.kaiji.model.player.bot.BotFactory;
import com.softserveinc.ita.kaiji.model.Card;
import com.softserveinc.ita.kaiji.model.player.HumanPlayer;
import com.softserveinc.ita.kaiji.model.player.Player;

/**
 * 
 * @author Bohdan Shaposhnik
 * @version 2.5
 * @since 17.03.2014
 */
@Service
public class GameServiceImpl implements GameService {

	private static Logger logger = Logger.getLogger(GameServiceImpl.class);

	private static final List<Game> GAMES = new ArrayList<Game>();
	private static final List<Player> PLAYERS = new ArrayList<Player>();
    private static final List<GameInfo> GAME_INFOS  = new ArrayList<>();

	@Autowired
	private GameFactory gameFactory;
    @Autowired
    private PlayerFactory playerFactory;

	private GameInfo setGameInfo(GameInfoDto gameInfoDto) {

		if (logger.isTraceEnabled()) {
			logger.trace("setGameInfo: we are in");
		}
		Set<Player> playersSet = new HashSet<Player>();

        //todo  normal id generation
        //todo normal user generation ))

        User user = new UserImpl(gameInfoDto.getPlayerName());

        //Now players should be made from Users
        Player realPlayer = playerFactory.makePlayer(user, gameInfoDto
                .getNumberOfCards());

		playersSet.add(realPlayer);

		if (gameInfoDto.isBotGame()) {


            //Bot is now created like player but with another params
			Player bot = playerFactory.makePlayer(gameInfoDto.getBotType(),
					gameInfoDto.getNumberOfCards());


			playersSet.add(bot);
			if (logger.isTraceEnabled()) {
				logger.trace("setGameInfo: bot added to the set of players");
			}
		}

		GameInfo newGameInfo = new GameInfoImpl(gameInfoDto.getGameName(),
				gameInfoDto.getPlayerName(), gameInfoDto.getNumberOfCards(),
				gameInfoDto.isBotGame(), gameInfoDto.getBotType(), playersSet);
        GAME_INFOS.add(newGameInfo);
		return newGameInfo;
	}

	@Override
	public void finishGame(Integer gameId) throws IllegalArgumentException{
		if (logger.isTraceEnabled()) {
			logger.trace("finishGame: started");
		}
		Game game = getGameById(gameId);
		game.finishGame();
		GAMES.remove(gameId);
		Integer playerId = getPlayerIdFromGame(gameId);
		PLAYERS.remove(playerId);
	}

	@Override
	public GameInfo getGameInfo(Integer gameId) throws IllegalArgumentException {
		return getGameById(gameId).getGameInfo();
	}

	@Override
	public Integer createGame(GameInfoDto gameInfoDto) {
		GameInfo gameInfo = setGameInfo(gameInfoDto);

        //todo now can be only bot game, must get game type from GameInfoDTO object
        if (gameInfoDto.getIsBotGame()) {
        gameInfo.setGameType(Game.Type.BOT_GAME);
        } else {
            gameInfo.setGameType(Game.Type.TWO_PLAYER_GAME);
        }
		Game game = gameFactory.makeGame(gameInfo);
		if (GAMES.contains(game)) {
			if (logger.isTraceEnabled()) {
				logger.trace("createGame: game is already in pool");
			}
			return game.getId();
		}
		GAMES.add(game);
		game.setId(GAMES.indexOf(game));
		if (logger.isTraceEnabled()) {
			logger.trace("createGame: game added");
		}
		addPlayers(game);
		return game.getId();
	}

	@Override
	public void makeTurn(Integer gameId, Integer playerId, Card card)
			throws IllegalArgumentException {
		Player activePlayer = null;
		for (Player player : PLAYERS) {
			if (player.getId() == playerId)
				activePlayer = player;
		}
		Game game = getGameById(gameId);
			for (Player player : game.getGameInfo().getPlayers()) {
				if (player.isBot()) {
					Bot bot = (Bot) player;
					bot.setGameData(game.getGameHistory());
				}
			}
			if (logger.isTraceEnabled()) {
				logger.trace("makeTurn: exucuting a turn");
			}
			game.makeTurn(card, activePlayer);

	}

	@Override
	public Game.State getGameStatus(Integer gameId)
			throws IllegalArgumentException {
		return getGameById(gameId).getState();
	}

	@Override
	public GameHistory getGameHistory(Integer gameId)
			throws IllegalArgumentException {
		return getGameById(gameId).getGameHistory();
	}

	// TODO: It's moving to PlayerService
	@Override
	public Player getPlayerById(Integer playerId) {
		for (Player player : PLAYERS) {
			if (player.getId().equals(playerId))
				return player;
		}
		return null;
	}

	@Override
	public Integer getPlayerIdFromGame(Integer gameId)
			throws IllegalArgumentException {
		Game game = getGameById(gameId);
		for (Player player : game.getGameInfo().getPlayers()) {
			if (!player.isBot()) {
				return player.getId();
			}
		}
		return null;
	}

	private Game getGameById(Integer gameId) throws IllegalArgumentException {
		for (Game game : GAMES) {
			if (game.getId().equals(gameId)) {
				if (logger.isTraceEnabled()) {
					logger.trace("findGameById: game found");
				}
				return game;
			}
			if (logger.isTraceEnabled()) {
				logger.trace("findGameById: game not found");
			}
		}
		throw new IllegalArgumentException(
				"There is no game with such id started!");
	}

	private void addPlayers(Game game) {
		GameInfo gameInfo = game.getGameInfo();
		for (Player player : gameInfo.getPlayers()) {

				PLAYERS.add(player);
                if (!player.isBot())
				player.setId(PLAYERS.indexOf(player));
				if (logger.isTraceEnabled()) {
					logger.trace("player added to pool");

			}
		}
	}

    @Override
    public void addPlayer(User user, String gameName) {
        for (GameInfo gi : GAME_INFOS) {
            if (gi.getGameName().equals(gameName)) {
                Player secondPlayer = playerFactory.makePlayer(user, gi.getNumberOfCards());
                System.out.println(secondPlayer);
                gi.getPlayers().add(secondPlayer);
                PLAYERS.add(secondPlayer);
                secondPlayer.setId(PLAYERS.indexOf(secondPlayer));
            }
        }

    }

    @Override
    public Integer getPlayerIdFromGame(String playerName, String gameName)
            throws IllegalArgumentException {
        for (GameInfo gi : GAME_INFOS) {
            if (gi.getGameName().equals(gameName)) {
              for (Player p: gi.getPlayers()) {
                  if (p.getName().equals(playerName)) {
                      return p.getId();
                  }
              }
            }

        }
        return null;
    }

    public Integer getGameId(String gameName) {
        for (Game g : GAMES) {
            if (g.getGameInfo().getGameName().equals(gameName)){
                return g.getId();
            }
        }
        return null;
    }

}
